跳到主要内容

11. Strict Mode

默认情况下,Pydantic 会强制将值转化成期望的数据类型(For example, you can pass the string "123" as the input to an int field, and it will be converted to 123),这种强制性的行为在很多场景下都是很有用的 — think: UUIDs, URL parameters, HTTP headers, environment variables, user input, etc.

不过,也有很多情况下,报错而不是强制转换是更为合适的行为

Pydantic 提供了 "strict mode" 来严格地检查数据类型,它可以应用在 model 级别、field 级别,甚至是单次验证级别。

from pydantic import BaseModel, ValidationError


class MyModel(BaseModel):
x: int


print(MyModel.model_validate({'x': '123'})) # lax mode
#> x=123

try:
MyModel.model_validate({'x': '123'}, strict=True) # strict mode
except ValidationError as exc:
print(exc)
"""
1 validation error for MyModel
x
Input should be a valid integer [type=int_type, input_value='123', input_type=str]
"""

Pydantic 提供了诸多方法来使用 strict-mode validation:

strict mode 中的类型强制转换

对大部分类型而言,在严格模式下验证时,只有此类型下的实例才会被接受。For example, when validating an int field, only instances of int are accepted; passing instances of float or str will result in raising a ValidationError.

不过从 JSON 中验证信息时,条件会更为宽松:For example, when validating a UUID field, instances of str will be accepted when validating from JSON, but not from python:

import json
from uuid import UUID

from pydantic import BaseModel, ValidationError


class MyModel(BaseModel):
guid: UUID


data = {'guid': '12345678-1234-1234-1234-123456789012'}

print(MyModel.model_validate(data)) # OK: lax
#> guid=UUID('12345678-1234-1234-1234-123456789012')

print(
MyModel.model_validate_json(json.dumps(data), strict=True)
) # OK: strict, but from json
#> guid=UUID('12345678-1234-1234-1234-123456789012')

try:
MyModel.model_validate(data, strict=True) # Not OK: strict, from python
except ValidationError as exc:
print(exc.errors(include_url=False))
"""
[
{
'type': 'is_instance_of',
'loc': ('guid',),
'msg': 'Input should be an instance of UUID',
'input': '12345678-1234-1234-1234-123456789012',
'ctx': {'class': 'UUID'},
}
]
"""

For more details about what types are allowed as inputs in strict mode, you can review the Conversion Table.

Strict mode in method calls

上述的所有例子都是通过在 validation methods 中传入 strict=True 来达成目的;你也可以在 TypeAdapter 传入这个参数,以期在验证单个值的时候启用严格模式:

from pydantic import TypeAdapter, ValidationError

print(TypeAdapter(bool).validate_python('yes')) # OK: lax
#> True

try:
TypeAdapter(bool).validate_python('yes', strict=True) # Not OK: strict
except ValidationError as exc:
print(exc)
"""
1 validation error for bool
Input should be a valid boolean [type=bool_type, input_value='yes', input_type=str]
"""

更复杂的类型也不在话下:

from dataclasses import dataclass

from pydantic import TypeAdapter, ValidationError


@dataclass
class MyDataclass:
x: int


try:
TypeAdapter(MyDataclass).validate_python({'x': '123'}, strict=True)
except ValidationError as exc:
print(exc)
"""
1 validation error for MyDataclass
Input should be an instance of MyDataclass [type=dataclass_exact_type, input_value={'x': '123'}, input_type=dict]
"""

This also works with the TypeAdapter.validate_json and BaseModel.model_validate_json methods:

import json
from typing import List
from uuid import UUID
from pydantic import BaseModel, TypeAdapter, ValidationError


try:
TypeAdapter(List[int]).validate_json('["1", 2, "3"]', strict=True)
except ValidationError as exc:
print(exc)
"""
2 validation errors for list[int]
0
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
2
Input should be a valid integer [type=int_type, input_value='3', input_type=str]
"""


class Model(BaseModel):
x: int
y: UUID


data = {'x': '1', 'y': '12345678-1234-1234-1234-123456789012'}
try:
Model.model_validate(data, strict=True)
except ValidationError as exc:
# Neither x nor y are valid in strict mode from python:
print(exc)
"""
2 validation errors for Model
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
y
Input should be an instance of UUID [type=is_instance_of, input_value='12345678-1234-1234-1234-123456789012', input_type=str]
"""

json_data = json.dumps(data)
try:
Model.model_validate_json(json_data, strict=True)
except ValidationError as exc:
# From JSON, x is still not valid in strict mode, but y is:
print(exc)
"""
1 validation error for Model
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""

Strict mode with Field

你可以 set strict=True on the field. 这样做可以为此字段单独开启严格模式,即使 validation method 中没有 strict=True

from pydantic import BaseModel, Field, ValidationError

class User(BaseModel):
name: str
age: int
n_pets: int


user = User(name='John', age='42', n_pets='1')
print(user)
#> name='John' age=42 n_pets=1


class AnotherUser(BaseModel):
name: str
age: int = Field(strict=True)
n_pets: int


try:
anotheruser = AnotherUser(name='John', age='42', n_pets='1')
except ValidationError as e:
print(e)
"""
1 validation error for AnotherUser
age
Input should be a valid integer [type=int_type, input_value='42', input_type=str]
"""

Note that making fields strict will also affect the validation performed when instantiating the model class:

from pydantic import BaseModel, Field, ValidationError


class Model(BaseModel):
x: int = Field(strict=True)
y: int = Field(strict=False)


try:
Model(x='1', y='2')
except ValidationError as exc:
print(exc)
"""
1 validation error for Model
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""

Using Field as an annotation

Field(strict=True) (或其他任何的关键字参数) 可以在 Annotated 中使用。e.g., when working with TypedDict:

from typing_extensions import Annotated, TypedDict
from pydantic import Field, TypeAdapter, ValidationError


class MyDict(TypedDict):
x: Annotated[int, Field(strict=True)]


try:
TypeAdapter(MyDict).validate_python({'x': '1'})
except ValidationError as exc:
print(exc)
"""
1 validation error for typed-dict
x
Input should be a valid integer [type=int_type, input_value='1', input_type=str]
"""

Strict mode with Annotated[..., Strict()]

Pydantic 还提供了 Strict class, 用来作 [Annotated] 的元数据; this annotation indicates that the annotated field should be validated in strict mode:

from typing_extensions import Annotated

from pydantic import BaseModel, Strict, ValidationError


class User(BaseModel):
name: str
age: int
is_active: Annotated[bool, Strict()]


User(name='David', age=33, is_active=True)
try:
User(name='David', age=33, is_active='True')
except ValidationError as exc:
print(exc)
"""
1 validation error for User
is_active
Input should be a valid boolean [type=bool_type, input_value='True', input_type=str]
"""

This is, in fact, the method used to implement some of the strict-out-of-the-box types provided by Pydantic, such as StrictInt.

Strict mode with ConfigDict

BaseModel